﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using Castle.DynamicProxy;
using FluentValidation;
using FluentValidation.Results;
using NUnit.Framework;
using NBehave.Spec.NUnit;
using TimeKeeper.Infrastructure;
using Moq;

namespace TimeKeeper.UnitTests.Infrastructure
{
    public abstract class when_object_is_intercepted_for_IDataErrorInfo_interface : Specification
    {
        public class Dummy
        {
            public virtual string String { get; set; }
        }

        protected Dummy UnderTest;

        protected Mock<IValidator> _validator;
        protected ValidationResult _validationResult;
        
        protected override void Establish_context()
        {
            base.Establish_context();

            _validator = new Mock<IValidator>();
            _validator.Setup(x => x.Validate(It.IsAny<object>())).Returns(_validationResult);

            var generator = new ProxyGenerator();
            UnderTest = (Dummy)generator.CreateClassProxy(typeof(Dummy), new[] { typeof(IDataErrorInfo), typeof(INotifyPropertyChanged) }, 
                new DataErrorInfoInterceptor(_validator.Object));
        }
    }

    public class and_validation_passed : when_object_is_intercepted_for_IDataErrorInfo_interface
    {
        private string _result;

        protected override void Establish_context()
        {
            _validationResult = new ValidationResult();
            base.Establish_context();
        }

        protected override void Because_of()
        {
            base.Because_of();
            UnderTest.String = "Something";
            _result = ((IDataErrorInfo)UnderTest)["String"];
        }

        [Test]
        public void then_validator_should_be_called_exactly_once()
        {
            _validator.Verify(x => x.Validate(UnderTest), Times.Exactly(1));
        }

        [Test]
        public void then_result_for_property_should_be_empty()
        {
            _result.ShouldBeEmpty();
        }
    }

    public class and_validation_fails : when_object_is_intercepted_for_IDataErrorInfo_interface
    {
        private const string ErrorMessage = "Something is wrong";
        private string _result;

        protected override void Establish_context()
        {
            _validationResult = new ValidationResult(new List<ValidationFailure> { new ValidationFailure("String", ErrorMessage)});
            base.Establish_context();
        }

        protected override void Because_of()
        {
            base.Because_of();
            UnderTest.String = "Something";
            _result = ((IDataErrorInfo) UnderTest)["String"];
        }

        [Test]
        public void then_result_for_property_should_not_be_empty()
        {
            _result.ShouldNotBeEmpty();
            _result.ShouldEqual(ErrorMessage);
        }
    }

    public class and_validation_is_triggered_twice_but_with_unchanged_property : when_object_is_intercepted_for_IDataErrorInfo_interface
    {
        protected override void Establish_context()
        {
            _validationResult = new ValidationResult();
            base.Establish_context();
        }

        protected override void Because_of()
        {
            base.Because_of();
            UnderTest.String = "Something";
            var r1 = ((IDataErrorInfo)UnderTest)["String"];
            UnderTest.String = "Something";
            var r2 = ((IDataErrorInfo)UnderTest)["String"];
        }

        [Test]
        public void then_validator_should_be_called_exactly_once()
        {
            _validator.Verify(x => x.Validate(UnderTest), Times.Exactly(1));
        }
    }

    public class and_validation_is_triggered_twice_but_with_changed_property : when_object_is_intercepted_for_IDataErrorInfo_interface
    {
        protected override void Establish_context()
        {
            _validationResult = new ValidationResult();
            base.Establish_context();
        }

        protected override void Because_of()
        {
            base.Because_of();
            UnderTest.String = "Something";
            var r1 = ((IDataErrorInfo)UnderTest)["String"];
            UnderTest.String = "Something else";
            var r2 = ((IDataErrorInfo)UnderTest)["String"];
        }

        [Test]
        public void then_validator_should_be_called_exactly_twice()
        {
            _validator.Verify(x => x.Validate(UnderTest), Times.Exactly(2));
        }
    }
}
